import csvimport reimport warningsfrom copy import copyfrom datetime import datetime, time, timedeltaimport loggingfrom logging.handlers import TimedRotatingFileHandlerimport osimport pandas as pdfrom openpyxl import Workbook, load_workbookfrom openpyxl.styles import Alignment, Border, Font, PatternFill, Sidefrom openpyxl.utils import get_column_letterfrom openpyxl.worksheet.datavalidation import DataValidation# 禁用 SettingWithCopyWarningwarnings.filterwarnings('ignore', category=pd.errors.SettingWithCopyWarning)# 或者更简单的方式：pd.options.mode.chained_assignment = Noneclass CellFormatter:    def __init__(self, worksheet, logger):        self.ws = worksheet        self.logger = logger        self.color_palette = [            "E6F3FF", "FFF2CC", "FFE6E6", "E6FFEE", "E6F9FF", "FFFDE6", "FFF9E6", "F3E6FF", "FFE6CC", "E6FFCC",            "FFE6F9", "E6FCFF", "FFF0E6", "E6FFF2", "FFE6E9", "E6F0FF", "FFE6D9", "E6FFFC", "FFE6FF", "E6FFE6",            "FFE6F0", "E6F6FF", "FFF6E6", "E6FFF9", "FFE6EC", "E6EDFF", "FFE6CC", "E6FFFF", "FFF0E6", "E6FFE9",            "FFE6F6", "E6FAFF", "FFFAE6", "E6FFF0", "FFE6E6", "E6EEFF", "FFE6D9", "E6FFFC", "FFE6FF", "E6FFE6",            "FFE6F3", "E6F9FF", "FFF3E6", "E6FFF6", "FFE6E9", "E6F3FF", "FFE6CC", "E6FFFF", "FFEDE6", "E6FFEC"        ]        self.course_colors = {}        self.color_index = 0    def format_worksheet(self):        """格式化整个工作表"""        self.logger.info("开始格式化工作表")        # 重新计算并设置行高        self._adjust_row_heights()        # 为课程分配颜色        self._assign_course_colors()        # 应用格式到所有单元格        self._apply_cell_formats()        # 设置边框        self._set_borders()        self.logger.info("工作表格式化完成")    def _adjust_row_heights(self):        """调整行高"""        self.logger.debug("开始调整行高")        # 设置标题行高度        self.ws.row_dimensions[1].height = 36        self.ws.row_dimensions[2].height = 24        # 调整内容行的高度        for row in range(3, self.ws.max_row + 1):            max_lines = 1            for col in range(2, 9):  # 周一到周日的列                cell = self.ws.cell(row = row, column = col)                if cell.value:                    lines = len(str(cell.value).split('\n'))                    max_lines = max(max_lines, lines)            # 根据行中最大行数设置行高            # 每行文字大约20个像素，额外添加一些padding            row_height = self._calculate_row_height(max_lines) if row == self.ws.max_row else 13            self.ws.row_dimensions[row].height = row_height            self.logger.debug(f"设置第{row}行高度为{row_height}, max_lines={max_lines}")    def _calculate_row_height(self, max_lines):        if max_lines == 1:            return 13        elif max_lines == 2:            return 27        elif max_lines == 3:            return 27 + 13  # 40        else:  # max_lines >= 4            return 13 * max_lines    def _assign_course_colors(self):        """为每个不同的课程分配颜色"""        self.logger.debug("开始分配课程颜色")        # 收集所有不同的课程名称        courses = set()        for row in range(3, self.ws.max_row + 1):            for col in range(2, 9):                cell = self.ws.cell(row = row, column = col)                if cell.value:                    # 获取课程名称（第一行文本）                    course_name = cell.value.split('\n')[0].strip()                    if '(' in course_name:  # 处理带括号的课程名                        course_name = course_name.split('(')[0].strip()                    courses.add(course_name)        # 为每个课程分配颜色        for course in sorted(courses):  # 排序以确保颜色分配的一致性            self.course_colors[course] = self.color_palette[self.color_index]            self.color_index = (self.color_index + 1) % len(self.color_palette)            self.logger.debug(f"为课程 '{course}' 分配颜色 {self.course_colors[course]}")    def _apply_cell_formats(self):        """应用单元格格式"""        self.logger.debug("开始应用单元格格式")        # 格式化标题        title_cell = self.ws.cell(row = 1, column = 1)        title_cell.font = Font(size = 18, bold = True)        title_cell.alignment = Alignment(horizontal = 'center', vertical = 'center')        # 格式化表头        for col in range(1, 9):            header_cell = self.ws.cell(row = 2, column = col)            header_cell.font = Font(color = "FFFFFF", bold = True)            header_cell.fill = PatternFill(start_color = "00a3f5", end_color = "00a3f5", fill_type = "solid")            header_cell.alignment = Alignment(horizontal = 'center', vertical = 'center')        # 格式化内容单元格        for row in range(3, self.ws.max_row + 1):            for col in range(1, 9):                cell = self.ws.cell(row = row, column = col)                if col == 1:  # 时间列                    self._format_time_cell(cell)                # elif col == 2:  # 上午/下午列                #     self._format_period_cell(cell)                else:  # 课程内容列                    self._format_course_cell(cell)    def _format_time_cell(self, cell):        """格式化时间列单元格"""        cell.alignment = Alignment(horizontal = 'center', vertical = 'center')        cell.font = Font(size = 10)    def _format_period_cell(self, cell):        """格式化上午/下午列单元格"""        cell.alignment = Alignment(horizontal = 'center', vertical = 'center')        cell.font = Font(size = 10, bold = True)    def _format_course_cell(self, cell):        """格式化课程内容单元格"""        if not cell.value:            return        # 设置对齐方式        cell.alignment = Alignment(horizontal = 'center', vertical = 'center', wrap_text = True)        # 获取课程名称并设置背景色        course_name = cell.value.split('\n')[0].strip()        if '(' in course_name:            course_name = course_name.split('(')[0].strip()        if course_name in self.course_colors:            cell.fill = PatternFill(                start_color = self.course_colors[course_name],                end_color = self.course_colors[course_name],                fill_type = "solid"            )        # 重新格式化课程信息        self._reformat_course_content(cell)    def _reformat_course_content(self, cell):        """重新格式化课程内容"""        if not cell.value:            return        lines = cell.value.split('\n')        formatted_lines = []        current_course = None        time_info = []        for line in lines:            line = line.strip()            if not line:                continue            if '-' not in line:  # 课程名                # 如果已有课程信息，先添加到结果中                if current_course and time_info:                    formatted_lines.append(current_course)                    formatted_lines.extend(sorted(time_info))                current_course = line                time_info = []            else:  # 时间信息                time_info.append(line)        # 添加最后一个课程的信息        if current_course and time_info:            formatted_lines.append(current_course)            formatted_lines.extend(sorted(time_info))        # 更新单元格内容        cell.value = '\n'.join(formatted_lines)    def _set_borders(self):        """设置单元格边框"""        thin_border = Border(            left = Side(style = 'thin'),            right = Side(style = 'thin'),            top = Side(style = 'thin'),            bottom = Side(style = 'thin')        )        for row in self.ws.iter_rows(min_row = 1, max_row = self.ws.max_row, min_col = 1, max_col = 8):            for cell in row:                cell.border = thin_borderclass CellMerger:    def __init__(self, worksheet, logger):        self.ws = worksheet        self.logger = logger    def merge_similar_cells(self):        """        扫描并合并包含相同课程的相邻单元格        """        self.logger.info("开始执行单元格合并操作")        # 从第3行开始扫描(前两行是标题)        for col in range(2, 9):  # 周一到周日的列            self.logger.info(f"\n处理第{get_column_letter(col)}列的单元格合并")            self._merge_column_cells(col)    def _merge_column_cells(self, col):        """处理一列中的单元格合并"""        row = 3  # 从第3行开始        while row < self.ws.max_row:            # 获取当前单元格的合并范围            current_range = self._get_merge_range(row, col)            if not current_range:                row += 1                continue            self.logger.debug(f"\n检查单元格 {get_column_letter(col)}{current_range.min_row}")            # 获取当前单元格的内容            current_content = self.ws.cell(row = current_range.min_row, column = col).value            if not current_content:                row = current_range.max_row + 1                continue            self.logger.debug(f"当前单元格内容:\n{current_content}")            # 获取下一个单元格的起始行号            next_row = current_range.max_row + 1            if next_row >= self.ws.max_row:                self.logger.debug("已到达最后一行，结束处理")                break            # 获取下一个单元格的合并范围            next_range = self._get_merge_range(next_row, col)            if not next_range:                row = next_row + 1                continue            # 获取下一个单元格的内容            next_content = self.ws.cell(row = next_range.min_row, column = col).value            if not next_content:                row = next_range.max_row + 1                continue            self.logger.debug(f"下一个单元格内容:\n{next_content}")            # 解析两个单元格的课程内容            current_courses = self._parse_course_content(current_content)            next_courses = self._parse_course_content(next_content)            self.logger.debug(f"当前单元格课程: {list(current_courses.keys())}")            self.logger.debug(f"下一个单元格课程: {list(next_courses.keys())}")            # 检查是否有相同课程            common_courses = set(current_courses.keys()) & set(next_courses.keys())            if common_courses:                self.logger.info(f"发现相同课程: {common_courses}")                self.logger.info(                    f"合并单元格 {get_column_letter(col)}{current_range.min_row} 和 {get_column_letter(col)}{next_range.min_row}")                # 合并这两个单元格                merged_content = self._merge_cell_contents(current_content, next_content)                # 取消原有的合并                self.logger.debug("取消原有的单元格合并")                if current_range:                    self.ws.unmerge_cells(                        start_row = current_range.min_row,                        start_column = col,                        end_row = current_range.max_row,                        end_column = col                    )                if next_range:                    self.ws.unmerge_cells(                        start_row = next_range.min_row,                        start_column = col,                        end_row = next_range.max_row,                        end_column = col                    )                # 创建新的合并单元格                total_rows = next_range.max_row - current_range.min_row + 1                self.ws.merge_cells(                    start_row = current_range.min_row,                    start_column = col,                    end_row = next_range.max_row,                    end_column = col                )                self.logger.debug(f"合并后的内容:\n{merged_content}")                # 填充合并后的内容                cell = self.ws.cell(row = current_range.min_row, column = col)                cell.value = merged_content                # 由于合并了单元格，从合并后的单元格继续检查                row = current_range.min_row                continue            else:                self.logger.debug("没有找到相同课程，继续检查下一个单元格")            row = next_range.max_row + 1    def _parse_course_content(self, content):        """解析单元格内容为课程字典"""        if not content:            return {}        courses = {}        current_course = None        for line in content.split('\n'):            line = line.strip()            if not line:                continue            if '-' not in line:  # 这是课程名                current_course = line.strip()                if '(' in current_course:  # 处理可能带有括号的课程名                    current_course = current_course.split('(')[0].strip()                courses[current_course] = []            else:  # 这是时间信息                if current_course:                    courses[current_course].append(line)        return courses    def _merge_cell_contents(self, content1, content2):        """合并两个单元格的内容，并按时间排序"""        # 解析两个单元格的内容        courses1 = self._parse_course_content(content1)        courses2 = self._parse_course_content(content2)        # 合并课程信息        merged_courses = {}        all_course_names = sorted(set(list(courses1.keys()) + list(courses2.keys())))        for course_name in all_course_names:            times = []            if course_name in courses1:                times.extend(courses1[course_name])            if course_name in courses2:                times.extend(courses2[course_name])            # 按时间排序，确保不重复            times = list(set(times))  # 去重            times.sort(key = lambda x: x.split('-')[0])  # 按开始时间排序            merged_courses[course_name] = times        # 生成合并后的内容字符串        merged_content = []        for course_name in all_course_names:            merged_content.append(course_name)            merged_content.extend(merged_courses[course_name])        return '\n'.join(merged_content)    def _get_merge_range(self, row, col):        """获取指定单元格的合并范围"""        for merged_range in self.ws.merged_cells.ranges:            if (row >= merged_range.min_row and row <= merged_range.max_row and                    col >= merged_range.min_col and col <= merged_range.max_col):                return merged_range        # 如果不是合并单元格，返回表示单个单元格的范围        from openpyxl.worksheet.merge import MergedCellRange        return MergedCellRange(self.ws,                               f"{self.ws.cell(row = row, column = col).coordinate}:{self.ws.cell(row = row, column = col).coordinate}")class CellSplitter:    def __init__(self, worksheet):        self.ws = worksheet    def split_course_cells(self):        """        扫描并拆分包含多门课程的单元格        """        # 从第3行开始扫描(前两行是标题)        for row in range(3, self.ws.max_row + 1):            for col in range(2, 9):  # 周一到周日的列                cell = self.ws.cell(row = row, column = col)                if self._is_merged_cell(row, col) and self._contains_multiple_courses(cell.value):                    # 获取当前合并单元格的范围                    merge_range = self._get_merge_range(row, col)                    if merge_range:                        # 拆分合并单元格                        self._split_merged_cell(merge_range, col)    def _is_merged_cell(self, row, col):        """检查是否是合并的单元格"""        for merged_range in self.ws.merged_cells.ranges:            if (row >= merged_range.min_row and row <= merged_range.max_row and                    col >= merged_range.min_col and col <= merged_range.max_col):                return True        return False    def _get_merge_range(self, row, col):        """获取合并单元格的范围"""        for merged_range in self.ws.merged_cells.ranges:            if (row >= merged_range.min_row and row <= merged_range.max_row and                    col >= merged_range.min_col and col <= merged_range.max_col):                return merged_range        return None    def _contains_multiple_courses(self, cell_value):        """检查单元格是否包含多门课程"""        if not cell_value:            return False        # 将单元格内容按换行符分割,并计算不同课程的数量        lines = cell_value.split('\n')        courses = set()        current_course = None        for line in lines:            if not line.strip():                continue            # 如果这一行不包含时间(不包含-),那么这是一个新的课程名            if '-' not in line:                current_course = line.strip()                courses.add(current_course)        return len(courses) > 1    def _parse_course_times(self, cell_value):        """解析单元格中的课程时间信息"""        lines = cell_value.split('\n')        courses = []        current_course = {}        for line in lines:            if not line.strip():                continue            if '-' not in line:  # 这是课程名                if current_course:                    courses.append(current_course.copy())                current_course = {'name': line.strip(), 'times': [], 'total_minutes': 0}            else:  # 这是时间信息                time_info = line.strip()                start_time = time_info.split('-')[0]                end_time = time_info.split('-')[1].split(' ')[0]                # 计算时长（分钟）                start_hour, start_min = map(int, start_time.split(':'))                end_hour, end_min = map(int, end_time.split(':'))                duration = (end_hour * 60 + end_min) - (start_hour * 60 + start_min)                current_course['total_minutes'] += duration                current_course['times'].append({                    'start': start_time,                    'end': end_time,                    'students': time_info.split(' ', 1)[1] if ' ' in time_info else '',                    'duration': duration                })        if current_course:            courses.append(current_course)        # 按最早开始时间排序        courses.sort(key = lambda x: min(t['start'] for t in x['times']))        return courses    def _split_merged_cell(self, merge_range, col):        """拆分合并的单元格"""        # 获取原始内容        original_content = self.ws.cell(row = merge_range.min_row, column = col).value        # 解析课程信息        courses = self._parse_course_times(original_content)        total_minutes = sum(course['total_minutes'] for course in courses)        # 计算每个课程应占用的行数        total_rows = merge_range.max_row - merge_range.min_row + 1        min_rows = 1  # 每个课程至少占用1行        # 先取消原来的合并        self.ws.unmerge_cells(            start_row = merge_range.min_row,            start_column = col,            end_row = merge_range.max_row,            end_column = col        )        # 分配行数并填充内容        current_row = merge_range.min_row        remaining_rows = total_rows        for i, course in enumerate(courses):            # 计算这个课程应该占用的行数            if i == len(courses) - 1:                # 最后一个课程使用剩余的所有行                rows_for_course = remaining_rows            else:                # 按比例分配行数,但至少保证1行                rows_for_course = max(                    min_rows,                    round(total_rows * (course['total_minutes'] / total_minutes))                )                rows_for_course = min(rows_for_course, remaining_rows - (len(courses) - i - 1))            # 生成这个课程的内容            course_content = [course['name']]            course_content.extend(f"{time['start']}-{time['end']} {time['students']}"                                  for time in course['times'])            # 合并单元格并填充内容            if rows_for_course > 1:                self.ws.merge_cells(                    start_row = current_row,                    start_column = col,                    end_row = current_row + rows_for_course - 1,                    end_column = col                )            # 填充内容            cell = self.ws.cell(row = current_row, column = col)            cell.value = '\n'.join(course_content)            # 更新行号和剩余行数            current_row += rows_for_course            remaining_rows -= rows_for_courseclass ScheduleGenerator:    def __init__(self, file_path, output_dir=None):        self.file_path = file_path        self.output_dir = output_dir        self.students = set()        self.sheet_input_schedule = '课程录入'        self.df = None        self.schedule = {day: [] for day in ['一', '二', '三', '四', '五', '六', '日']}        self.student_columns = ['上课学生一', '上课学生二', '上课学生三', '上课学生四', '上课学生五']        self.course_colors = {}        self.color_palette = [            "E6F3FF", "FFF2CC", "FFE6E6", "E6FFEE", "E6F9FF", "FFFDE6", "FFF9E6", "F3E6FF", "FFE6CC", "E6FFCC",            "FFE6F9", "E6FCFF", "FFF0E6", "E6FFF2", "FFE6E9", "E6F0FF", "FFE6D9", "E6FFFC", "FFE6FF", "E6FFE6",            "FFE6F0", "E6F6FF", "FFF6E6", "E6FFF9", "FFE6EC", "E6EDFF", "FFE6CC", "E6FFFF", "FFF0E6", "E6FFE9",            "FFE6F6", "E6FAFF", "FFFAE6", "E6FFF0", "FFE6E6", "E6EEFF", "FFE6D9", "E6FFFC", "FFE6FF", "E6FFE6",            "FFE6F3", "E6F9FF", "FFF3E6", "E6FFF6", "FFE6E9", "E6F3FF", "FFE6CC", "E6FFFF", "FFEDE6", "E6FFEC"        ]        self.weekday_colors = [            "F0F8FF", "F0FFF0", "FFF0F5", "F0FFFF", "FFF5EE", "F5FFFA", "FFFAF0"        ]        self.color_index = 0        self.course_info = {}  # 新增：用于存储课程信息        self.setup_logging()    def set_date_range(self, date_range):        """设置日期范围"""        self.date_range = date_range        self.logger.info(f"设置日期范围: {date_range}")    def _create_title_with_date(self, name):        """创建带有日期范围的标题"""        if self.date_range:            return f"{name} ({self.date_range})"        return name    def set_output_dir(self, output_dir):        self.output_dir = output_dir    def setup_logging(self, log_dir='logs'):        # 创建日志目录（如果不存在）        if not os.path.exists(log_dir):            os.makedirs(log_dir)        # 设置日志文件名（每天一个新文件）        log_file = os.path.join(log_dir, f"schedule_generator_{datetime.now().strftime('%Y-%m-%d')}.log")        # 创建 logger        self.logger = logging.getLogger('schedule_generator')        self.logger.setLevel(logging.DEBUG)        # 创建 TimedRotatingFileHandler        file_handler = TimedRotatingFileHandler(            log_file, when="midnight", interval=1, backupCount=30        )        file_handler.setLevel(logging.DEBUG)        # 创建 StreamHandler（控制台输出）        console_handler = logging.StreamHandler()        console_handler.setLevel(logging.INFO)        # 设置日志格式        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')        file_handler.setFormatter(formatter)        console_handler.setFormatter(formatter)        # 添加 handlers        self.logger.addHandler(file_handler)        self.logger.addHandler(console_handler)    def log_exception(self, message):        self.logger.error(message, exc_info=True)    def get_course_at_time(self, time, day):        for start, end, course in self.schedule[day]:            if start <= time.time() < end:                return course, start, end        return "", None, None    def merge_cells_if_needed(self, ws, row, col, course_name):        # 向上查找相同课程        start_row = row        while start_row > 2 and ws.cell(row = start_row - 1, column = col).value == course_name:            start_row -= 1        # 向下查找相同课程        end_row = row        while end_row < ws.max_row and ws.cell(row = end_row + 1, column = col).value == course_name:            end_row += 1        # 如果找到了需要合并的单元格，进行合并        if start_row < end_row:            ws.merge_cells(start_row = start_row, start_column = col, end_row = end_row, end_column = col)            # 只在合并后的第一个单元格中保留值            for r in range(start_row + 1, end_row + 1):                ws.cell(row = r, column = col).value = None    def generate_category_schedule(self, category, courses):        self.logger.info(f"开始生成 {category} 类别的课表")        self.logger.info(f"该类别包含的课程: {courses}")        try:            course_df = self.df[self.df['课程'].isin(courses)]            if course_df.empty:                self.logger.info(f"No courses found for category: {category}")                self.logger.info(f"Available courses in DataFrame: {self.df['课程'].unique()}")                return None, f"提示：未找到 {category} 类别的任何排课记录。"            course_df['开始时间'] = pd.to_datetime(course_df['开始时间'], format = '%H:%M:%S').dt.time            course_df['结束时间'] = pd.to_datetime(course_df['结束时间'], format = '%H:%M:%S').dt.time            wb = Workbook()            ws = wb.active            ws.title = f"{category}课程表"            # 设置列宽            ws.column_dimensions['A'].width = 15            for col in range(2, 9):                ws.column_dimensions[get_column_letter(col)].width = 25            # 添加标题            ws.merge_cells('A1:H1')            title_cell = ws.cell(row = 1, column = 1, value = f"{category}课程表 {self.date_range}")            title_cell.font = Font(size = 16, bold = True)            title_cell.alignment = Alignment(horizontal = 'center', vertical = 'center')            # 添加日期行            days = ['时间', '周一', '周二', '周三', '周四', '周五', '周六', '周日']            for col, day in enumerate(days, start = 1):                cell = ws.cell(row = 2, column = col, value = day)                cell.font = Font(color = "FFFFFF", bold = True)                cell.alignment = Alignment(horizontal = 'center', vertical = 'center')                cell.fill = PatternFill(start_color = "BBCCE4", end_color = "BBCCE4", fill_type = "solid")            # 生成时间槽            time_slots = self.generate_time_slots()            self.logger.debug(f"生成的时间槽数量: {len(time_slots)}")            # 创建一个二维数组来存储课程信息            schedule_array = [['' for _ in range(8)] for _ in range(len(time_slots))]            # 填充时间槽列            for i, start_time in enumerate(time_slots[:-1]):                end_time = time_slots[i + 1]                schedule_array[i][0] = f"{start_time.strftime('%H:%M')}-{end_time.strftime('%H:%M')}"            # 按星期填充课程信息            for day_index, day in enumerate(['一', '二', '三', '四', '五', '六', '日'], start = 1):                day_courses = course_df[course_df['周几'] == day].sort_values('开始时间')                course_groups = []                current_group = []                # 首先将课程分组                for _, course in day_courses.iterrows():                    if not current_group:                        current_group.append(course)                        continue                    # 检查是否与当前组中的任何课程存在时间重叠                    has_overlap = False                    for existing_course in current_group:                        if self.has_time_overlap(                                existing_course['开始时间'], existing_course['结束时间'],                                course['开始时间'], course['结束时间']                        ):                            has_overlap = True                            break                    if has_overlap:                        current_group.append(course)                    else:                        course_groups.append(current_group)                        current_group = [course]                # 添加最后一个组                if current_group:                    course_groups.append(current_group)                # 处理每个课程组                for group in course_groups:                    # 找出组的起止时间                    group_start_time = min(course['开始时间'] for course in group)                    group_end_time = max(course['结束时间'] for course in group)                    self.logger.debug(f"\n处理课程组:")                    self.logger.debug(f"组起始时间: {group_start_time}")                    self.logger.debug(f"组结束时间: {group_end_time}")                    # 获取对齐后的开始和结束时间                    aligned_start_time = self.get_aligned_time(group_start_time, is_end_time = False)                    aligned_end_time = self.get_aligned_time(group_end_time, is_end_time = True)                    self.logger.debug(f"开始时间对齐后: {aligned_start_time}")                    self.logger.debug(f"结束时间对齐后: {aligned_end_time}")                    # 获取时间槽索引                    start_slot = self.get_slot_index(aligned_start_time, time_slots, is_end_time = False)                    end_slot = self.get_slot_index(aligned_end_time, time_slots, is_end_time = True)                    self.logger.debug(f"最终时间槽范围: {start_slot} -> {end_slot}")                    # 组织课程信息                    course_info = {}                    for course in group:                        course_name = course['课程']                        students = [str(course[col]) for col in self.student_columns if pd.notna(course[col])]                        time_info = f"{course['开始时间'].strftime('%H:%M')}-{course['结束时间'].strftime('%H:%M')} {' '.join(students)}"                        if course_name not in course_info:                            course_info[course_name] = []                        course_info[course_name].append(time_info)                    # 格式化课程信息                    formatted_info = []                    for course_name, times in course_info.items():                        formatted_info.append(course_name)                        formatted_info.extend(sorted(times))                    # 填充到时间槽                    formatted_text = '\n'.join(formatted_info)                    for i in range(start_slot, end_slot + 1):                        if i < len(schedule_array):                            schedule_array[i][day_index] = formatted_text            # 将数组信息填充到Excel中            for row, row_data in enumerate(schedule_array, start = 3):                for col, cell_value in enumerate(row_data, start = 1):                    ws.cell(row = row, column = col, value = cell_value)                    ws.cell(row = row, column = col).alignment = Alignment(wrapText = True, horizontal = 'center',                                                                           vertical = 'center')            # 检查并删除最后一行空行（如果存在）            if ws.cell(row = ws.max_row, column = 2).value == "":                ws.delete_rows(ws.max_row)            # 合并相同内容的连续单元格            for col in range(2, 9):  # 从周一到周日                start_row = 3                for row in range(4, ws.max_row + 1):                    if ws.cell(row = row, column = col).value != ws.cell(row = row - 1, column = col).value:                        if row - start_row > 1:                            ws.merge_cells(start_row = start_row, start_column = col, end_row = row - 1,                                           end_column = col)                        start_row = row                # 处理最后一组单元格                if ws.max_row - start_row + 1 > 1:                    ws.merge_cells(start_row = start_row, start_column = col, end_row = ws.max_row, end_column = col)            # 为内容相同的非空单元格统一上色            course_colors = {}            color_index = 0            for row in range(3, ws.max_row + 1):                for col in range(2, 9):                    cell = ws.cell(row = row, column = col)                    if cell.value:                        content = cell.value.strip().split('\n')[0]  # 使用第一行（课程名）作为键                        if content not in course_colors:                            course_colors[content] = self.color_palette[color_index % len(self.color_palette)]                            color_index += 1                        cell.fill = PatternFill(start_color = course_colors[content],                                                end_color = course_colors[content],                                                fill_type = "solid")                    else:                        # 空单元格使用白色填充                        cell.fill = PatternFill(start_color = "FFFFFF",                                                end_color = "FFFFFF",                                                fill_type = "solid")            # 5. 合并时间列的特定单元格            ws.merge_cells('A3:A4')  # 合并 08:30-08:45 和 08:45-09:00            ws.merge_cells('A16:A19')  # 合并 11:45-12:00 至 12:30-12:45            ws.cell(row = 3, column = 1).value = '08:30-09:00'            ws.cell(row = 16, column = 1).value = '11:45-12:45'            # 设置边框            for row in ws.iter_rows(min_row = 1, max_row = ws.max_row, min_col = 1, max_col = 8):                for cell in row:                    cell.border = Border(left = Side(style = 'thin'),                                         right = Side(style = 'thin'),                                         top = Side(style = 'thin'),                                         bottom = Side(style = 'thin'))            # 在调用CellSplitter之前，先进行单元格合并            cell_merger = CellMerger(ws, self.logger)            cell_merger.merge_similar_cells()            # 在保存文件之前添加            cell_splitter = CellSplitter(ws)            cell_splitter.split_course_cells()            # 重新格式化            cell_formatter = CellFormatter(ws, self.logger)            cell_formatter.format_worksheet()            # 保存文件            try:                output_file = os.path.join(self.output_dir, f'{category}课程表.xlsx')                wb.save(output_file)                self.logger.info(f"{category}课程表已保存至: {output_file}")                return output_file, None            except Exception as e:                error_msg = f"保存 {category} 课程表时遇到错误：{str(e)}。请确保您有权限写入该文件夹，并且磁盘空间充足。"                self.logger.error(error_msg)                return None, error_msg        except Exception as e:            error_msg = f"生成 {category} 类别课表时遇到意外错误：{str(e)}。请检查输入数据是否正确。"            self.logger.error(error_msg)            return None, error_msg    def get_slot_index(self, target_time, time_slots, is_end_time = False):        """        获取时间对应的时间槽索引        Parameters:            target_time: datetime.time - 目标时间            time_slots: list - 时间槽列表            is_end_time: bool - 是否是结束时间        Returns:            int - 对应时间槽的索引        """        self.logger.debug(f"\n查找时间槽 - 目标时间: {target_time}, 是否是结束时间: {is_end_time}")        # 转换为datetime以便比较        target_dt = datetime.combine(datetime.today(), target_time)        # 找到对应的时间槽        for i, slot in enumerate(time_slots[:-1]):            slot_dt = datetime.combine(datetime.today(), slot.time())            next_slot_dt = datetime.combine(datetime.today(), time_slots[i + 1].time())            if is_end_time:                # 对于结束时间，使用小于等于比较                if slot_dt < target_dt <= next_slot_dt:                    self.logger.debug(f"找到结束时间匹配时间槽 - 索引: {i}, "                                      f"时间槽范围: {slot.time()}-{time_slots[i + 1].time()}")                    return i            else:                # 对于开始时间，保持半开区间                if slot_dt <= target_dt < next_slot_dt:                    self.logger.debug(f"找到开始时间匹配时间槽 - 索引: {i}, "                                      f"时间槽范围: {slot.time()}-{time_slots[i + 1].time()}")                    return i        # 处理边界情况        if target_dt <= datetime.combine(datetime.today(), time_slots[0].time()):            self.logger.debug(f"时间在首个时间槽之前，返回索引 0")            return 0        self.logger.debug(f"时间在最后时间槽之后，返回索引 {len(time_slots) - 2}")        return len(time_slots) - 2    def get_aligned_time(self, original_time, is_end_time = False):        """        将时间对齐到15分钟间隔        开始时间向下取整，结束时间向上取整        Parameters:            original_time: datetime.time - 原始时间            is_end_time: bool - 是否是结束时间        Returns:            datetime.time - 对齐后的时间        """        # 转换为datetime以便计算        dt = datetime.combine(datetime.today(), original_time)        original_minutes = dt.hour * 60 + dt.minute        # 计算分钟数        minutes = original_minutes        if is_end_time:            # 对于结束时间，向上取整            aligned_minutes = ((minutes + 14) // 15) * 15            self.logger.debug(f"结束时间对齐: 原始时间={original_time}, "                              f"原始分钟数={original_minutes}, "                              f"对齐后分钟数={aligned_minutes}, "                              f"对齐方式=向上取整")        else:            # 对于开始时间，向下取整            aligned_minutes = (minutes // 15) * 15            self.logger.debug(f"开始时间对齐: 原始时间={original_time}, "                              f"原始分钟数={original_minutes}, "                              f"对齐后分钟数={aligned_minutes}, "                              f"对齐方式=向下取整")        # 计算新的小时和分钟        new_hour = aligned_minutes // 60        new_minute = aligned_minutes % 60        # 创建新的时间        aligned_time = dt.replace(hour = new_hour, minute = new_minute, second = 0).time()        # 处理边界情况        earliest = time(8, 30)        latest = time(18, 0)        final_time = aligned_time        if aligned_time < earliest:            final_time = earliest            self.logger.debug(f"时间调整到早边界: {aligned_time} -> {earliest}")        elif aligned_time > latest:            final_time = latest            self.logger.debug(f"时间调整到晚边界: {aligned_time} -> {latest}")        self.logger.debug(f"最终对齐时间: {final_time}")        return final_time    def has_time_overlap(self, time1_start, time1_end, time2_start, time2_end):        """        检查两个时间段是否存在重叠        Parameters:            time1_start, time1_end, time2_start, time2_end: datetime.time        Returns:            bool: 是否存在重叠        """        # 转换为datetime以便比较        today = datetime.today()        t1_start = datetime.combine(today, time1_start)        t1_end = datetime.combine(today, time1_end)        t2_start = datetime.combine(today, time2_start)        t2_end = datetime.combine(today, time2_end)        return max(t1_start, t2_start) < min(t1_end, t2_end)    def merge_cells_with_same_content(self, ws):        for col in range(2, 9):  # 从周一到周日            merge_start = 3            current_value = ws.cell(row = 3, column = col).value            for row in range(4, ws.max_row + 1):                cell_value = ws.cell(row = row, column = col).value                if cell_value != current_value:                    if row - merge_start > 1:                        ws.merge_cells(start_row = merge_start, start_column = col, end_row = row - 1, end_column = col)                        merged_cell = ws.cell(row = merge_start, column = col)                        merged_cell.alignment = Alignment(wrapText = True, horizontal = 'center', vertical = 'center')                    merge_start = row                    current_value = cell_value            # 处理最后一组单元格            if ws.max_row - merge_start + 1 > 1:                ws.merge_cells(start_row = merge_start, start_column = col, end_row = ws.max_row, end_column = col)                merged_cell = ws.cell(row = merge_start, column = col)                merged_cell.alignment = Alignment(wrapText = True, horizontal = 'center', vertical = 'center')    def set_borders(self, ws):        thin_border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))        for row in ws.iter_rows(min_row=1, max_row=ws.max_row, min_col=1, max_col=8):            for cell in row:                cell.border = thin_border    def color_courses(self, ws):        course_colors = {}        used_colors = set()        for row in range(3, ws.max_row + 1):            for col in range(2, 9):                cell = ws.cell(row = row, column = col)                if cell.value:                    content = cell.value.strip().split('\n')[0]  # 使用第一行作为键                    if content not in course_colors:                        available_colors = set(self.color_palette) - used_colors                        if not available_colors:                            used_colors.clear()                            available_colors = set(self.color_palette)                        color = available_colors.pop()                        course_colors[content] = color                        used_colors.add(color)                    cell.fill = PatternFill(start_color = course_colors[content],                                            end_color = course_colors[content],                                            fill_type = "solid")                else:                    # 空单元格使用白色填充                    cell.fill = PatternFill(start_color = "FFFFFF",                                            end_color = "FFFFFF",                                            fill_type = "solid")    def generate_schedule(self, child_name):        child_df = self.filter_child_schedule(child_name)        self.schedule = {day: [] for day in ['一', '二', '三', '四', '五', '六', '日']}  # 重置 schedule        self.populate_schedule(child_df)        return self.create_excel(child_name)    def generate_color(self):        color = self.color_palette[self.color_index]        self.color_index = (self.color_index + 1) % len(self.color_palette)        return color    def fill_schedule(self, ws, time_slots):        for col, day in enumerate(['一', '二', '三', '四', '五', '六', '日'], start = 2):            current_course = None            merge_start = None            for i, start_time in enumerate(time_slots[:-1]):                end_time = time_slots[i + 1]                row = i + 2  # Excel行号                # 获取在这个时间段内的所有课程                day_courses = [course for course in self.schedule[day] if                               (course[0] < end_time.time() and course[1] > start_time.time())]                if day_courses:                    course = day_courses[0]  # 取第一个课程（如果有多个重叠的课程）                    course_name, course_start, course_end = course[2], course[0], course[1]                    if course_name != current_course:                        if current_course:                            # 合并之前的单元格                            if merge_start and merge_start < row:                                ws.merge_cells(start_row = merge_start, start_column = col, end_row = row - 1,                                               end_column = col)                                merged_cell = ws.cell(row = merge_start, column = col)                                merged_cell.alignment = Alignment(horizontal = 'center', vertical = 'center',                                                                  wrap_text = True)                        current_course = course_name                        merge_start = row                        # 填充新课程信息                        cell = ws.cell(row = row, column = col)                        cell.value = f"{course_name}\n{course_start.strftime('%H:%M')}-{course_end.strftime('%H:%M')}"                        if course_name not in self.course_colors:                            self.course_colors[course_name] = self.generate_color()                        cell.fill = PatternFill(start_color = self.course_colors[course_name],                                                end_color = self.course_colors[course_name], fill_type = "solid")                else:                    if current_course:                        # 合并之前的单元格                        if merge_start and merge_start < row:                            ws.merge_cells(start_row = merge_start, start_column = col, end_row = row - 1,                                           end_column = col)                            merged_cell = ws.cell(row = merge_start, column = col)                            merged_cell.alignment = Alignment(horizontal = 'center', vertical = 'center',                                                              wrap_text = True)                        current_course = None                        merge_start = None                # 设置单元格边框                cell = ws.cell(row = row, column = col)                cell.border = Border(left = Side(style = 'thin'), right = Side(style = 'thin'),                                     top = Side(style = 'thin'), bottom = Side(style = 'thin'))            # 处理每天最后一个课程的合并            if current_course and merge_start and merge_start < row:                ws.merge_cells(start_row = merge_start, start_column = col, end_row = row, end_column = col)                merged_cell = ws.cell(row = merge_start, column = col)                merged_cell.alignment = Alignment(horizontal = 'center', vertical = 'center', wrap_text = True)        # 设置时间列        for i, start_time in enumerate(time_slots[:-1]):            row = i + 2            ws.cell(row = row, column = 1,                    value = f"{start_time.strftime('%H:%M')}-{time_slots[i + 1].strftime('%H:%M')}")        # 删除多余的空行        while ws.max_row > 2 and all(                ws.cell(row = ws.max_row, column = col).value is None for col in range(1, ws.max_column + 1)):            ws.delete_rows(ws.max_row)    def merge_cells(self, ws):        for col in range(2, 9):  # 遍历周一到周日            start_row = 2            current_course = None            for row in range(2, ws.max_row + 1):                cell_value = ws.cell(row = row, column = col).value                if cell_value != current_course:                    if current_course and row - start_row > 1:                        ws.merge_cells(start_row = start_row, start_column = col, end_row = row - 1, end_column = col)                    current_course = cell_value                    start_row = row            # 处理最后一组相同的课程            if current_course and ws.max_row - start_row + 1 > 1:                ws.merge_cells(start_row = start_row, start_column = col, end_row = ws.max_row, end_column = col)    def generate_time_slots(self):        start = datetime.strptime("08:30", "%H:%M")        end = datetime.strptime("18:00", "%H:%M")        time_slots = []        while start <= end:            time_slots.append(start)            start += timedelta(minutes=15)        return time_slots    def add_headers(self, ws):        headers = ["时间"] + [f"周{day}" for day in ['一', '二', '三', '四', '五', '六', '日']]        for col, header in enumerate(headers, start = 1):            cell = ws.cell(row = 1, column = col, value = header)            cell.fill = PatternFill(start_color = "DDDDDD", end_color = "DDDDDD", fill_type = "solid")            cell.border = Border(left = Side(style = 'thin'), right = Side(style = 'thin'), top = Side(style = 'thin'),                                 bottom = Side(style = 'thin'))            cell.alignment = Alignment(horizontal = 'center', vertical = 'center')    def read_excel(self):        try:            # 使用上下文管理器打开Excel文件            with pd.ExcelFile(self.file_path) as xlsx:                # 尝试读取 '课程录入' 工作表                if '课程录入' in xlsx.sheet_names:                    self.df = pd.read_excel(xlsx, '课程录入')                else:                    self.df = pd.read_excel(xlsx, 0)  # 使用第一个工作表                # 尝试读取 '课程类别' 工作表                if '课程类别' in xlsx.sheet_names:                    self.categories_df = pd.read_excel(xlsx, '课程类别')                elif len(xlsx.sheet_names) > 1:                    self.categories_df = pd.read_excel(xlsx, 1)  # 使用第二个工作表                else:                    raise ValueError("找不到课程类别信息")            # 清理数据和提取学生信息            self.df.columns = self.df.columns.str.strip()            self.df['周几'] = self.df['周几'].str.strip()            self.extract_students()            self.logger.info(f"成功读取课程数据，共 {len(self.df)} 条记录")            self.logger.info(f"成功读取课程类别数据，共 {len(self.categories_df)} 个类别")            self.logger.debug(f"课程类别: {self.categories_df['课程类别名称'].tolist()}")        except Exception as e:            self.logger.error(f"读取Excel文件时出错: {str(e)}")            raise ValueError(f"读取Excel文件时出错: {str(e)}")    def extract_students(self):        for column in self.student_columns:            self.students.update(self.df[column].dropna().unique())    def filter_child_schedule(self, child_name):        def safe_contains(x, name):            return x.astype(str).str.contains(name, na=False)        child_df = self.df[            self.df[self.student_columns].apply(lambda x: safe_contains(x, child_name)).any(axis=1)]        return child_df    def check_schedule_conflicts(self):        conflicts = {}        all_students = set()        for col in self.student_columns:            all_students.update(self.df[col].dropna().unique())        for student in all_students:            student_df = self.df[                self.df[self.student_columns].apply(                    lambda x: x.astype(str).str.contains(student, na = False, regex = False)).any(axis = 1)]            for day in ['一', '二', '三', '四', '五', '六', '日']:                day_courses = student_df[student_df['周几'] == day]                for i, course1 in day_courses.iterrows():                    for j, course2 in day_courses.iterrows():                        if i < j:  # 避免重复检查                            try:                                start1 = self.parse_time(course1['开始时间'])                                end1 = self.parse_time(course1['结束时间'])                                start2 = self.parse_time(course2['开始时间'])                                end2 = self.parse_time(course2['结束时间'])                                if max(start1, start2) < min(end1, end2):                                    if student not in conflicts:                                        conflicts[student] = []                                    conflict_info = {                                        '星期': f"星期{day}",                                        '课程1': f"{course1['课程']} ({self.format_time(start1)}-{self.format_time(end1)})",                                        '课程2': f"{course2['课程']} ({self.format_time(start2)}-{self.format_time(end2)})",                                        '行数1': i + 2,  # Excel行数从1开始，且有表头                                        '行数2': j + 2                                    }                                    conflicts[student].append(conflict_info)                                    self.logger.warning(f"检测到冲突：{student} - {conflict_info}")                            except ValueError as e:                                self.logger.error(                                    f"时间解析错误 - 学生: {student}, 日期: {day}, 课程1: {course1['课程']} ({course1['开始时间']}-{course1['结束时间']}), 课程2: {course2['课程']} ({course2['开始时间']}-{course2['结束时间']}), 错误: {str(e)}")        # 记录冲突信息        if conflicts:            self.logger.warning("发现课程冲突：")            for student, student_conflicts in conflicts.items():                self.logger.warning(f"{student}的课程冲突：")                for conflict in student_conflicts:                    self.logger.warning(                        f"  {conflict['星期']}: {conflict['课程1']} 与 {conflict['课程2']} 时间冲突【第{conflict['行数1']}行和第{conflict['行数2']}行】")        return conflicts    def parse_time(self, time_str):        try:            # 首先尝试解析 HH:MM:SS 格式            return datetime.strptime(str(time_str), "%H:%M:%S").time()        except ValueError:            try:                # 如果失败，尝试解析 HH:MM 格式                return datetime.strptime(str(time_str), "%H:%M").time()            except ValueError as e:                self.logger.error(f"无法解析时间: {time_str}, 错误: {str(e)}")                raise    def generate_all_category_schedules(self):        if self.df is None or self.categories_df is None:            raise ValueError("请先读取Excel文件")        generated_schedules = []        for _, row in self.categories_df.iterrows():            category = row['课程类别名称']            courses = [course.strip() for course in re.split(r'[、,，\s]+', row['课程列表'])]            self.logger.info(f"正在处理类别: {category}，包含课程: {courses}")            try:                output_file, error_msg = self.generate_category_schedule(category, courses)                generated_schedules.append((category, output_file, error_msg))            except Exception as e:                error_msg = f"生成 {category} 类别课表时遇到意外错误：{str(e)}。请检查输入数据是否正确。"                self.logger.error(error_msg)                generated_schedules.append((category, None, error_msg))        return generated_schedules    def generate_all_schedules(self):        if self.df is None:            self.read_excel()        if self.output_dir is None:            raise ValueError("输出目录未设置，请先设置输出目录。")        conflicts = self.check_schedule_conflicts()        if conflicts:            return None, conflicts        generated_schedules = []        # 生成学生个人课表        for student in self.students:            try:                output_file = self.generate_schedule(student)                self.logger.info(f"Generated schedule for {student}")                generated_schedules.append(('student', student, output_file, None))            except Exception as e:                error_msg = f"生成 {student} 的课表时遇到意外错误：{str(e)}。请检查该学生的课程数据是否完整。"                self.log_exception(error_msg)                generated_schedules.append(('student', student, None, error_msg))        # 生成课程类别课表        for _, row in self.categories_df.iterrows():            category = row['课程类别名称']            courses = [course.strip() for course in re.split(r'[、,，\s]+', row['课程列表'])]            self.logger.info(f"正在处理类别: {category}，包含课程: {courses}")            try:                output_file, error_msg = self.generate_category_schedule(category, courses)                generated_schedules.append(('category', category, output_file, error_msg))            except Exception as e:                error_msg = f"生成 {category} 类别课表时遇到意外错误：{str(e)}。请检查输入数据是否正确。"                self.logger.error(error_msg)                generated_schedules.append(('category', category, None, error_msg))        return generated_schedules, None    def set_column_widths(self, ws):        ws.column_dimensions['A'].width = 15        for col in range(2, 9):            ws.column_dimensions[get_column_letter(col)].width = 20    def create_excel(self, child_name):        try:            wb = Workbook()            ws = wb.active            ws.title = "原始数据"            self.set_column_widths(ws)            self.add_headers(ws)            time_slots = self.generate_time_slots()            self.fill_schedule(ws, time_slots)            new_ws = self.beautify_schedule(wb, ws, child_name)            # 删除原始数据工作表            wb.remove(ws)            output_file = os.path.join(self.output_dir, f'{child_name}一周课表.xlsx')            wb.save(output_file)            self.logger.info(f"课程表已生成并保存到 '{output_file}' 文件中。")            return output_file        except Exception as e:            self.log_exception(f"Error creating Excel file for {child_name}")            raise    def find_course_info(self, day, course_name, start_time):        start_time = datetime.strptime(start_time, "%H:%M").time()        for key, info in self.course_info.items():            if (key[0] == day and                    key[1] == course_name and                    datetime.strptime(key[2], "%H:%M").time() <= start_time < datetime.strptime(key[3],                                                                                                "%H:%M").time()):                return info        return None    def format_time(self, time):        return time.strftime("%H:%M")    def populate_schedule(self, child_df):        for _, row in child_df.iterrows():            day = row['周几']            start_time = row['开始时间']            end_time = row['结束时间']            course = row['课程']            if day not in self.schedule:                self.logger.info(f"警告: 未知的星期几 '{day}'，已跳过")                continue            self.schedule[day].append((start_time, end_time, course))            # 存储课程信息，使用正确的列名            students = [str(row[f'上课学生{i}']) for i in ['一', '二', '三', '四', '五'] if                        pd.notna(row[f'上课学生{i}'])]            info = f"{start_time.strftime('%H:%M')} 至 {end_time.strftime('%H:%M')}, 上课学生为: {' '.join(students)}"            key = (day, course, start_time.strftime('%H:%M'), end_time.strftime('%H:%M'))            self.course_info[key] = info    def beautify_schedule(self, wb, ws, child_name, is_category_schedule=False):        # 创建新的工作表        new_ws = wb.create_sheet(title = f"{child_name}的课表")        # 1. 添加标题行        new_ws.merge_cells(f'A1:{get_column_letter(ws.max_column + 1)}1')        title_cell = new_ws.cell(row = 1, column = 1)        title_cell.value = f"{child_name}的课表 {self.date_range}"        title_cell.font = Font(size = 18, bold = True)        title_cell.alignment = Alignment(horizontal = 'center', vertical = 'center')        # 2. 复制原有数据到新工作表，并在最前面添加一个空列        for row in ws.iter_rows(min_row = 1, max_row = ws.max_row, min_col = 1, max_col = ws.max_column):            for cell in row:                new_cell = new_ws.cell(row = cell.row + 1, column = cell.column + 1)                new_cell.value = cell.value                if cell.has_style:                    new_cell._style = copy(cell._style)        # 3. 更新合并单元格        for merged_range in ws.merged_cells.ranges:            min_col, min_row, max_col, max_row = merged_range.min_col, merged_range.min_row, merged_range.max_col, merged_range.max_row            new_range = f"{get_column_letter(min_col + 1)}{min_row + 1}:{get_column_letter(max_col + 1)}{max_row + 1}"            new_ws.merge_cells(new_range)        # 4. 修正列标题        headers = ["", "时间", "周一", "周二", "周三", "周四", "周五", "周六", "周日"]        for col, header in enumerate(headers, start = 1):            new_ws.cell(row = 2, column = col, value = header)        # 5. 合并时间列的特定单元格        new_ws.merge_cells('B3:B4')  # 合并 08:30-08:45 和 08:45-09:00        new_ws.merge_cells('B16:B19')  # 合并 11:45-12:00 至 12:30-12:45        new_ws.cell(row = 3, column = 2).value = '08:30-09:00'        new_ws.cell(row = 16, column = 2).value = '11:45-12:45'        # 6. 调整列宽        new_ws.column_dimensions[get_column_letter(1)].width = 5  # 新增列的宽度        new_ws.column_dimensions[get_column_letter(2)].width = 15  # 时间列的宽度        for i in range(3, ws.max_column + 2):            new_ws.column_dimensions[get_column_letter(i)].width = 20  # 其他列的宽度        # 7. 设置第二行（列标题行）的背景颜色        for col in range(1, new_ws.max_column + 1):            cell = new_ws.cell(row = 2, column = col)            cell.fill = PatternFill(start_color = "FFA07A", end_color = "FFA07A", fill_type = "solid")  # 使用蓝色背景            # 设置文字为白色            cell.font = Font(color = "FFFFFF", bold = True)            # 设置对齐方式            cell.alignment = Alignment(horizontal = 'center', vertical = 'center')        new_ws.row_dimensions[2].height = 30        # 8. 在底部添加新行并格式化        if not is_category_schedule:            new_row = new_ws.max_row + 1            new_ws.cell(row = new_row, column = 2, value = "其他活动")            merge_range = f'{get_column_letter(3)}{new_row}:{get_column_letter(new_ws.max_column)}{new_row}'            new_ws.merge_cells(merge_range)        # 9. 重新设置边框和对齐方式，并调整行高        for row in new_ws.iter_rows(min_row = 3, max_row = new_ws.max_row, min_col = 1, max_col = new_ws.max_column):            for cell in row:                if cell.value:                    cell.border = Border(left = Side(style = 'thin'),                                         right = Side(style = 'thin'),                                         top = Side(style = 'thin'),                                         bottom = Side(style = 'thin'))                    cell.alignment = Alignment(horizontal = 'center', vertical = 'center', wrap_text = True)        # 10. 设置第一列的格式        for cell in new_ws['A']:            cell.border = Border(left = Side(style = 'thin'),                                 right = Side(style = 'thin'),                                 top = Side(style = 'thin'),                                 bottom = Side(style = 'thin'))            cell.alignment = Alignment(horizontal = 'center', vertical = 'center', wrap_text = True)        # 11. 设置最后一行的格式        last_row = new_ws.max_row        for cell in new_ws[last_row]:            cell.border = Border(left = Side(style = 'thin'),                                 right = Side(style = 'thin'),                                 top = Side(style = 'thin'),                                 bottom = Side(style = 'thin'))            cell.alignment = Alignment(horizontal = 'center', vertical = 'center', wrap_text = True)        # 10. 调整第一行和第二行的高度        new_ws.row_dimensions[1].height = 36  # 设置第一行（标题行）的高度为36        new_ws.row_dimensions[2].height = 24  # 设置第二行（列标题行）的高度为24        new_ws.merge_cells('A3:A15')        new_ws.merge_cells('A16:A19')        new_ws.merge_cells('A20:A40')        new_ws.cell(row = 3, column = 1).value = '上午'        new_ws.cell(row = 20, column = 1).value = '下午'        # 返回新的工作表        return new_ws